// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/net/InetAddress.h>

#include <muduo/base/Logging.h>
#include <muduo/net/Endian.h>
#include <muduo/net/SocketsOps.h>

#include <netdb.h>
#include <strings.h>  // bzero
#include <netinet/in.h>

// INADDR_ANY use (type)value casting.
#pragma GCC diagnostic ignored "-Wold-style-cast"
static const in_addr_t kInaddrAny = INADDR_ANY;
static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK;
#pragma GCC diagnostic error "-Wold-style-cast"

//     /* Structure describing an Internet socket address.  */
//     struct sockaddr_in {
//         sa_family_t    sin_family; /* address family: AF_INET */
//         uint16_t       sin_port;   /* port in network byte order */
//         struct in_addr sin_addr;   /* internet address */
//     };

//     /* Internet address. */
//     typedef uint32_t in_addr_t;
//     struct in_addr {
//         in_addr_t       s_addr;     /* address in network byte order */
//     };

//     struct sockaddr_in6 {
//         sa_family_t     sin6_family;   /* address family: AF_INET6 */
//         uint16_t        sin6_port;     /* port in network byte order */
//         uint32_t        sin6_flowinfo; /* IPv6 flow information */
//         struct in6_addr sin6_addr;     /* IPv6 address */
//         uint32_t        sin6_scope_id; /* IPv6 scope-id */
//     };

using namespace muduo;
using namespace muduo::net;

static_assert( sizeof( InetAddress ) == sizeof( struct sockaddr_in6 ),
	"InetAddress is same size as sockaddr_in6" );
static_assert( offsetof( sockaddr_in, sin_family ) == 0, "sin_family offset 0" );
static_assert( offsetof( sockaddr_in6, sin6_family ) == 0, "sin6_family offset 0" );
static_assert( offsetof( sockaddr_in, sin_port ) == 2, "sin_port offset 2" );
static_assert( offsetof( sockaddr_in6, sin6_port ) == 2, "sin6_port offset 2" );

#if !(__GNUC_PREREQ (4,6))
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
InetAddress::InetAddress( uint16_t port, bool loopbackOnly, bool ipv6 )
{
	static_assert( offsetof( InetAddress, addr6_ ) == 0, "addr6_ offset 0" );
	static_assert( offsetof( InetAddress, addr_ ) == 0, "addr_ offset 0" );
	if ( ipv6 )
	{
		bzero( &addr6_, sizeof addr6_ );
		addr6_.sin6_family = AF_INET6;
		in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;
		addr6_.sin6_addr = ip;
		addr6_.sin6_port = sockets::hostToNetwork16( port );
	}
	else
	{
		bzero( &addr_, sizeof addr_ );
		addr_.sin_family = AF_INET;
		in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
		addr_.sin_addr.s_addr = sockets::hostToNetwork32( ip );
		addr_.sin_port = sockets::hostToNetwork16( port );
	}
}

InetAddress::InetAddress( StringArg ip, uint16_t port, bool ipv6 )
{
	if ( ipv6 )
	{
		bzero( &addr6_, sizeof addr6_ );
		sockets::fromIpPort( ip.c_str(), port, &addr6_ );
	}
	else
	{
		bzero( &addr_, sizeof addr_ );
		sockets::fromIpPort( ip.c_str(), port, &addr_ );
	}
}

string InetAddress::toIpPort() const
{
	char buf[64] = "";
	sockets::toIpPort( buf, sizeof buf, getSockAddr() );
	return buf;
}

string InetAddress::toIp() const
{
	char buf[64] = "";
	sockets::toIp( buf, sizeof buf, getSockAddr() );
	return buf;
}

uint32_t InetAddress::ipNetEndian() const
{
	assert( family() == AF_INET );
	return addr_.sin_addr.s_addr;
}

uint16_t InetAddress::toPort() const
{
	return sockets::networkToHost16( portNetEndian() );
}

static __thread char t_resolveBuffer[64 * 1024];

bool InetAddress::resolve( StringArg hostname, InetAddress* out )
{
	assert( out != NULL );
	struct hostent hent;
	struct hostent* he = NULL;
	int herrno = 0;
	bzero( &hent, sizeof( hent ) );

	int ret = gethostbyname_r( hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno );
	if ( ret == 0 && he != NULL )
	{
		assert( he->h_addrtype == AF_INET && he->h_length == sizeof( uint32_t ) );
		out->addr_.sin_addr = *reinterpret_cast< struct in_addr* >( he->h_addr );
		return true;
	}
	else
	{
		if ( ret )
		{
			LOG_SYSERR << "InetAddress::resolve";
		}
		return false;
	}
}

bool InetAddress::operator<( const InetAddress& other ) const
{
	const struct sockaddr_in6* thisAddr = this->getSockAddr6();
	const struct sockaddr_in6* otherAddr = other.getSockAddr6();
	if ( thisAddr->sin6_family == AF_INET )
	{
		const struct sockaddr_in* laddr4 = reinterpret_cast< const struct sockaddr_in* >( thisAddr );
		const struct sockaddr_in* raddr4 = reinterpret_cast< const struct sockaddr_in* >( otherAddr );
		if ( laddr4->sin_addr.s_addr < raddr4->sin_addr.s_addr )
		{
			return true;
		}
		else if ( laddr4->sin_port < raddr4->sin_port )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else if ( thisAddr->sin6_family == AF_INET6 )
	{
		if ( memcmp( &thisAddr->sin6_addr, &otherAddr->sin6_addr, sizeof thisAddr->sin6_addr ) < 0 )
		{
			return true;
		}
		else if ( thisAddr->sin6_port < otherAddr->sin6_port )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		return false;
	}
}